package org.lboot.flow.processor.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Validator;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.lboot.flow.constant.FlowNodeTypeConst;
import org.lboot.flow.constant.FlowWorkStatusConst;
import org.lboot.flow.model.FlowModel;
import org.lboot.flow.model.node.BaseModel;
import org.lboot.flow.model.node.ParallelGatewayModel;
import org.lboot.flow.module.process.FlowProcess;
import org.lboot.flow.module.process.FlowProcessService;
import org.lboot.flow.module.work.FlowWork;
import org.lboot.flow.module.work.FlowWorkService;
import org.lboot.flow.module.work.params.FlowTaskParams;
import org.lboot.flow.parser.FlowNodeParser;
import org.lboot.flow.processor.FlowNodeProcessor;
import org.lboot.flow.event.system.FlowForkEvent;
import org.lboot.flow.event.system.FlowMergeEvent;
import org.lboot.flow.event.system.FlowProcessEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
public class ParallelGatewayNodeProcessorImpl implements FlowNodeProcessor {
    // 流程服务
    FlowWorkService flowWorkService;

    FlowNodeParser flowNodeParser;

    // 流程流转服务
    FlowProcessService flowProcessService;


    @Resource
    ApplicationContext context;

    @Override
    public String getNodeType() {
        return FlowNodeTypeConst.PARALLEL_GATEWAY_NODE;
    }


    @SneakyThrows
    @Override
    @Transactional
    public void process(FlowWork flowWork, FlowTaskParams taskParams) {
        FlowWork oldFlow = new FlowWork();
        BeanUtil.copyProperties(flowWork,oldFlow, CopyOptions.create().setIgnoreError(true).setIgnoreNullValue(true));
        // 流程执行到下一步
        BaseModel currentNode = flowWork.getNodeModel(flowWork.getCurrentNodeId());
        // 获取节点类型
        ParallelGatewayModel gatewayModel = (ParallelGatewayModel) currentNode;
        // 获取网关节点策略
        String strategy = gatewayModel.getStrategy();
        // 获取网关所需节点
        List<String> nodeIds = gatewayModel.getNodeIds();
        // 如果是join
        if (strategy.equals("join")){
            String flowWorkId = flowWork.getRootFlowId();
            if (Validator.isEmpty(flowWorkId)){
                //throw new BusinessException(HttpStatus.INTERNAL_SERVER_ERROR,"非分支流程无法被合并");
                log.error("非分支流程[{}:{}]无法被合并",flowWork.getFlowName(),flowWork.getId());
            }
            // 获取全部分支流程列表 SQL
            List<FlowWork> flowWorks = flowWorkService.getByRootFlowId(flowWorkId);
            // 获取全部分支流程ID列表
            List<String> subFlowIds = flowWorks.stream().map(FlowWork::getId).collect(Collectors.toList());
            // 查询全部分支流程流转信息 SQL
            List<FlowProcess> processes = flowProcessService.getByFlowIds(subFlowIds);
            // 匹配流程
            List<String> matchFlowIds = new ArrayList<>();
            // 查询匹配流程流转信息
            List<FlowProcess> matchProcesses = processes.stream().filter(ele-> {
                // 满足该条件的流转信息
                if ( nodeIds.contains(ele.getFromNode()) && ele.getToNode().equals(gatewayModel.getId())){
                    matchFlowIds.add(ele.getFlowWorkId());
                    return true;
                }
                return false;
            }).collect(Collectors.toList());
            // 获取匹配流程
            List<FlowWork> matchFlows = flowWorks.stream().filter(ele->{
                return matchFlowIds.contains(ele.getId());
            }).collect(Collectors.toList());


            // 打印
            log.info(matchFlowIds.toString());
            // 总分支流程 - 合并流程 = 0 恢复父流程执行
            if (matchFlows.size() == nodeIds.size()){
                // 总分支流程 - 合并流程 > 0 新建流程继续执行
                if (flowWorks.size() > matchFlows.size()){
                    FlowWork newFlow = new FlowWork();
                    BeanUtil.copyProperties(flowWork,newFlow,CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                    newFlow.setId(null);
                    // 设置当前
                    BaseModel nextModel = flowWork.getNodeModel(gatewayModel.getNextNodeId());
                    newFlow.setCurrentNodeId(nextModel.getId());
                    newFlow.setNextNodeId(nextModel.getNextNodeId());
                    // 清理全部合并流程
                    flowWorkService.batchDelete(matchFlowIds);
                    // 新建流程
                    FlowWork flowCreateRes = flowWorkService.saveOne(newFlow);
                    // 流程流转信息
                    context.publishEvent(new FlowProcessEvent(this,oldFlow,flowCreateRes,taskParams));
                    // 流程合并信息
                    context.publishEvent(new FlowMergeEvent(this,matchFlows,flowCreateRes,taskParams));

                }else {
                    FlowWork rootFlow = flowWorkService.getOneUnwrap(flowWorkId);
                    FlowWork oldRootFlow = new FlowWork();
                    BeanUtil.copyProperties(rootFlow,oldRootFlow);
                    // 设置当前
                    BaseModel nextModel = flowWork.getNodeModel(gatewayModel.getNextNodeId());
                    rootFlow.setCurrentNodeId(nextModel.getId());
                    rootFlow.setCurrentNodeType(nextModel.getNodeType());
                    rootFlow.setNextNodeId(nextModel.getNextNodeId());
                    rootFlow.setFlowWorkStatus(FlowWorkStatusConst.IN_PROGRESS);
                    rootFlow.setStatus(1);
                    // 清理全部合并流程
                    flowWorkService.batchDelete(matchFlowIds);
                    // 更新流程
                    FlowWork exRes = flowWorkService.updateOne(rootFlow);
                    // 流程流转信息
                    context.publishEvent(new FlowProcessEvent(this,oldRootFlow,rootFlow,taskParams));
                    // 流程合并 / 恢复信息
                    context.publishEvent(new FlowMergeEvent(this,matchFlows,exRes,taskParams));
                }
            }
            else {
                // 不符合流转条件
                log.info("等待更多分支流程");
                // 调整流程实例状态
                flowWork.setFlowWorkStatus(FlowWorkStatusConst.IN_WAIT);
                flowWork.setStatus(0);
                flowWorkService.updateOne(flowWork);
            }
        }else if (strategy.equals("fork")){
            // 如果处于等待状态 -> 说明已经执行完 -> 不再执行
            if (flowWork.getFlowWorkStatus().equals(FlowWorkStatusConst.IN_WAIT)){
                return;
            }
            // 流程分发
            List<FlowWork> flowWorks = new ArrayList<>();
            for (String id:nodeIds){
                // 构建流程名称
                String flowName = flowWork.getFlowName() + ":[" + id + "]并行流程";
                FlowWork subFlow = new FlowWork();
                BeanUtil.copyProperties(flowWork,subFlow,CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                subFlow.setId(null);
                BaseModel baseModel = flowWork.getNodeModel(id);
                subFlow.setFlowName(flowName);
                subFlow.setCurrentNodeId(id);
                subFlow.setCurrentNodeType(baseModel.getNodeType());
                subFlow.setNextNodeId(baseModel.getNextNodeId());
                // 为空
                subFlow.setRootFlowId(flowWork.getId());
                flowWorks.add(subFlow);
            }
            // 批量写入流程
            List<FlowWork> subFlows = flowWorkService.batchSave(flowWorks);
            // 发布事件
            context.publishEvent(new FlowForkEvent(this,subFlows,flowWork,taskParams));
            // 设置流程状态为等待
            flowWork.setFlowWorkStatus(FlowWorkStatusConst.IN_WAIT);
            // 更新流程实例状态
            flowWorkService.updateOne(flowWork);
        }
    }

    @Override
    public void back(FlowWork flowWork, FlowTaskParams taskParams) {

    }
}
